package main

import (
	"fmt"
	"log"
	"sync"

	//"log"
	"strings"

	"github.com/kataras/iris"
	"github.com/kataras/iris/sessions"
	"github.com/kataras/iris/websocket"
	pg "gopkg.in/pg.v5"
)

type Site struct {
	Prefix  string
	Rooms   []string
	Db      *pg.DB
	Sesns   *sessions.Sessions
	App     *iris.Application
	Clients sync.Map
	Ws      *websocket.Server
}

func New(app *iris.Application, sesns *sessions.Sessions, db *pg.DB, conf map[string]interface{}) interface{} {
	h := new(Site)
	p := conf["Prefix"]
	h.Prefix = p.(string)
	h.Rooms = []string{}
	p = conf["Rooms"]
	for _, p1 := range p.([]interface{}) {
		h.Rooms = append(h.Rooms, p1.(string))
	}
	h.Db = db
	h.App = app
	h.Sesns = sesns
	return h
}

func (site1 *Site) Run() error {
	app := site1.App
	ws := websocket.New(websocket.Config{
		ReadBufferSize:  1024,
		WriteBufferSize: 1024,
	})
	site1.Ws = ws
	ws.OnConnection(site1.handleConnection)

	app.Get("/chat/serve", ws.Handler())

	app.Get("/chat/room", func(ctx iris.Context) {
		if ctx.GetCookie("logined") != "yes" {
			ctx.Redirect("/user/")
			return
		}
		log.Printf("ws:%s\n", ctx.Host())
		ctx.ViewData("", iris.Map{"Prefix": "", "rooms": site1.Rooms,
			"title": "chat Page", "host": ctx.Host(), "SessionID": ctx.GetCookie("sessionid")})
		ctx.View("chat.html")
	})
	app.Get("/history/{roomName:string}/{pageNumber:long}", site1.showHistory)
	app.Get("/history/{roomName:string}/s", site1.findHistory)
	return nil
}

type chatClient struct {
	Id    string
	Name  string
	Room  string
	c     websocket.Connection
	Sitep *Site
}

func (site1 *Site) handleConnection(c websocket.Connection) {
	s1 := site1.Sesns.Start(c.Context())
	if s1.GetString("logined") != "yes" {
		c.Context().Redirect("/user/")
		c.Disconnect()
		log.Println("not logined")
		return
	}
	client := new(chatClient)
	client.Room = site1.Rooms[0]
	//client.Name = s1.GetString("chatname")
	client.Id = c.ID()
	client.Sitep = site1
	site1.Clients.Store(client.Id, client)

	client.OnConnected(c)
	//log.Printf("client: %s\n", client.Name)
}

func (client *chatClient) getNamesInRoom(room string) []string {
	conns := client.Sitep.Ws.GetConnectionsByRoom(room)
	if conns == nil {
		return nil
	}
	var ret = []string{}
	for _, c := range conns {
		n1, ok := client.Sitep.Clients.Load(c.ID())
		if ok {
			ret = append(ret, n1.(*chatClient).Name)
		}
	}
	return ret
}

func (client *chatClient) OnConnected(c websocket.Connection) {
	//初始化，加入默认聊天室：“聊天室”
	client.c = c
	//event chat {"msg":...,"to":...}
	c.On("chat", client.onChat)
	c.On("join", client.onJoin)
	c.On("ping", func(msg string) {
		c.To(client.Id).Emit("pong", "ok")
		//log.Printf("ping from %s\n", client.Id)
	})
	c.On("init", func(msg string) {
		c.Context().SetCookieKV("sessionid", msg)
		s1 := client.Sitep.Sesns.Start(c.Context())
		client.Name = s1.GetString("chatname")
		c.Join(client.Room)
		c.To(client.Room).Emit("join", fmt.Sprintf("%s %s", client.Name, client.Room))
		names := client.getNamesInRoom(client.Room)
		if names == nil {
			return
		}
		namesMsg := strings.Join(names, " ")
		c.To(client.Id).Emit("names", namesMsg)
	})
	c.OnDisconnect(client.onDisconnect)
}

func (client *chatClient) onChat(msg string) {
	client.c.To(client.Room).Emit("chat", fmt.Sprintf("%s 说 %s", client.Name, msg))
	sql1 := "insert into history(name,room,msg) values(?,?,?);"
	client.Sitep.Db.ExecOne(sql1, client.Name, client.Room, msg)
}

//onJoin 参数msg = room
func (client *chatClient) onJoin(msg string) {
	//广播leave，然后leave
	client.c.To(client.Room).Emit("leave", fmt.Sprintf("%s %s", client.Name, client.Room))
	client.c.Leave(client.Room)
	//join新的room
	client.Room = msg
	client.c.Join(client.Room)
	//广播join信息
	client.c.To(client.Room).Emit("join", fmt.Sprintf("%s %s", client.Name, client.Room))
	//names event　返回成员列表
	names := client.getNamesInRoom(client.Room)
	if names == nil {
		return
	}
	namesMsg := strings.Join(names, " ")
	client.c.To(client.Id).Emit("names", namesMsg)
}

func (client *chatClient) onDisconnect() {
	client.c.To(client.Room).Emit("leave", fmt.Sprintf("%s %s", client.Name, client.Room))
	client.Sitep.Clients.Delete(client.Id)
}
